package gui.actions;

import gui.GuiManager;
import gui.interfaces.WorkerActionInterface;
import gui.panels.InfiniteProgressPanel;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.sql.SQLTransactionRollbackException;

import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.SwingWorker;

public abstract class WorkerAction extends AbstractAction implements WorkerActionInterface {
	
	private static final long serialVersionUID = 1L;
    private static final long MINIMUM_OPERATION_TIME_MS = 100;
    private static final long NUMBER_OF_TRANSACTION_RETRIES = 3;
	private final Component owner;
	private final InfiniteProgressPanel glassPane;
	private boolean isOperationSucceed;
	private JFrame currFrame;
	
	public WorkerAction() {
		this(GuiManager.getCurrentFrame(), GuiManager.getCurrentFrame());
	}
	
	public WorkerAction(Component owner) {
		this(GuiManager.getCurrentFrame(),owner);
	}
	
	public WorkerAction(JFrame currFrame, Component owner) {
		this.currFrame = currFrame;
		this.owner = owner;
		glassPane = GuiManager.getGlassPane();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (currFrame == null) {
			currFrame = GuiManager.getCurrentFrame();
		}
		workerDo(false);
	}
	
	private void workerDo(final boolean isCommit) {
		boolean allow = isCommit ? isAllowToRunCommitOperation() : isAllowToRunOperation();
		if (allow) {
			SwingWorker<Exception, Void> worker = new SwingWorker<Exception, Void>() {

				@Override
				protected Exception doInBackground() throws Exception {
				    Exception ex = null;
				    long start = System.currentTimeMillis();
				    int retries = 0;
				    while(retries < NUMBER_OF_TRANSACTION_RETRIES) {
    					try {
    						if (isCommit) {
    							runCommitOperation();
    						} else {
    							runOperation();
    						}
    						break;
    					} catch(Exception e) {
    					    ex = e;
    					    if (e instanceof SQLTransactionRollbackException) {
    					        // wait before jumping back in
    					        Thread.sleep(100);
    					        retries++;
    					        continue;
    					    } else {
    					        break;
    					    }
    					}
				    }
					long runningTime = System.currentTimeMillis() - start;
					if (runningTime < MINIMUM_OPERATION_TIME_MS) {
					    try {
					        Thread.sleep(MINIMUM_OPERATION_TIME_MS - runningTime);
					    } catch (InterruptedException e) { /* do nothing */ }
					}
					return ex;
				}
				
				@Override
				protected void done() {
					super.done();
					glassPane.stop();
					try {
						Exception e = get();
						if (e == null) {
							isOperationSucceed = true;
							if (isCommit) {
								commitOperationSucceed();
							} else {
								operationSucceed();
								workerDo(true);
							}
						} else {
							isOperationSucceed = false;
							if (isCommit) {
								commitOperationFailed(e);
							} else {
								operationFailed(e);
							}
							workerDo(true);
						}
					} catch (Exception e) {
						if (isCommit) {
							commitOperationFailed(e);
						} else {
							operationFailed(e);
						}
					} 	
				}
				
			};	
			glassPane.start();	
			worker.execute();
		}
	}
	
	public Component getOwner() {
		return owner;
	}
	
	public boolean isOperationSucceed() {
		return isOperationSucceed;
	}
	
	public boolean isOperationFailed() {
		return !isOperationSucceed;
	}
	
	// Adapter implementation for the interface:
	
	public boolean isAllowToRunOperation() {
		return false;
	}
	
	public void runOperation() throws Exception {}
	
	public void operationSucceed() {}
	
	public void operationFailed(Exception e) {}
	
	public boolean isAllowToRunCommitOperation() {
		return false;
	}

	public void runCommitOperation() throws Exception {}
	
	public void commitOperationSucceed() {}
	
	public void commitOperationFailed(Exception e) {}
	
	//**********************************************
}
